package cn.org.wifit.imserver.boot;

import java.io.File;
import java.io.FileFilter;
import java.util.HashMap;
import java.util.regex.Pattern;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import cn.org.wifit.core.zookeeper.manager.ZookeeperServerBootWriteManager;

/**
 * 主要的启动类，在这里主要进行spring的初始化、netty的初始化、zookeeper的初始化
 * @author yinwenjie
 */
public class BootStartup {
	public static final HashMap<String, String> LOCAL_Environment = new HashMap<String , String>(); 
	
	public static FileSystemXmlApplicationContext context;
	
	/**
	 * 启动参数里面最主要有几个配置参数 
	 * -C 指定配置文件根目录的位置：如果没有记录，那么 
	 * -U 用于和各种客户端通信的UDP端口。这个端口必须指定 
	 * -T 用于和用户中心用来进行TCP长连接通信的端口。这个端口必须指定 
	 * -P 用于指定这台服务器的外网IP，以便客户端或者其他服务器能够访问
	 * @param args 
	 */
	public static void main(String[] args) {
		//注意获取当前jar文件所在的位置的方式为：new File("").getAbsolutePath();
		/*
		 * 首先查看变量信息
		 * */
		try {
			for(int index = 0 ; args != null && index < args.length ; index++) {
				if(args[index] == "-C") {
					BootStartup.LOCAL_Environment.put("-C", args[index + 1]);
				} else if(args[index].equals("-U")) {
					BootStartup.LOCAL_Environment.put("-U", args[index + 1]);
				} else if(args[index].equals("-T")) {
					BootStartup.LOCAL_Environment.put("-T", args[index + 1]);
				}  else if(args[index].equals("-P")) {
					BootStartup.LOCAL_Environment.put("-P", args[index + 1]);
				}
			}
			
			//检查各参数，并将没有的参数附上默认值
			if(BootStartup.LOCAL_Environment.get("-C") == null 
				|| BootStartup.LOCAL_Environment.get("-C").trim().equals("")) {
				String resourceRootPath = new File("resource").getAbsolutePath();
				BootStartup.LOCAL_Environment.put("-C" , resourceRootPath);
			} 
			
			//默认的和客户端进行UDP通信的端口5888
			if(BootStartup.LOCAL_Environment.get("-U") == null 
				|| BootStartup.LOCAL_Environment.get("-U").trim().equals("")) {
				BootStartup.LOCAL_Environment.put("-U" , "5888");
			}
			
			//默认的和各用户中心进行TCP长连接的端口6888
			if(BootStartup.LOCAL_Environment.get("-T") == null 
				|| BootStartup.LOCAL_Environment.get("-T").trim().equals("")) {
				BootStartup.LOCAL_Environment.put("-T" , "6888");
			}
			
			//默认的和客户端进行UDP通信的端口5888
			if(BootStartup.LOCAL_Environment.get("-P") == null 
				|| BootStartup.LOCAL_Environment.get("-P").trim().equals("")) {
				throw new RuntimeException("请指定一个外网IP，以便客户端或者其他服务器能够访问！");
			}
		} catch(Exception e) {
			System.out.println(e.getMessage() + " : 请检查启动参数");
			System.exit(0);
		}
		
		/*
		 * 参数OK了以后，需要完成相关的启动工作，顺序为：
		 * 0、初始化spring的各种配置文件，以便完成spring托管
		 * 1、初始化数据库连接
		 * 2、启动自身的UDP服务（及各种本地内存数据结构记录方式）
		 * 3、启动自身的TCP服务（及各种本地内存数据结构记录方式）
		 * 4、启动zookeeper连接，建立监听。（及各种本地内存数据结构记录方式）
		 * 5、在zookeeper响应位置，创建自身节点，并获取监听，
		 * */
		BootStartup.context = BootStartup.startupSpring();
		BootStartup bootStartup = (BootStartup)BootStartup.context.getBean("bootStartup");
		try {
			bootStartup.startManager(BootStartup.context);
		} catch(Exception e) {
			e.printStackTrace(System.out);
			System.exit(0);
		}
	}
	
	private StartupNettyUDPBoot startupNettyUDPBoot;
	
	private StartupNettyTCPBoot startupNettyTCPBoot;
	
	public BootStartup() {
		
	}
	
	/**
	 * 启动管理方法
	 */
	private void startManager(ApplicationContext springContext) throws Exception {
		//执行UDP服务，主守护线程的创建
		ThreadPoolTaskExecutor poolExecutor = (ThreadPoolTaskExecutor)springContext.getBean("UDPServerMainTheadPool");
		poolExecutor.execute(this.startupNettyUDPBoot);
		
		//执行TCP服务，主守护线程的创建
		poolExecutor = (ThreadPoolTaskExecutor)springContext.getBean("TCPServerMainTheadPool");
		poolExecutor.execute(this.startupNettyTCPBoot);
		
		//启动zookeeper连接，并建立代表自己已上线的节点，注意，必须UDP和TCP连接建立成功后，才能在启动的zookeeper集群上创建节点
		this.startupNettyUDPBoot.sync();
		this.startupNettyTCPBoot.sync();
		
		//========================
		this.startupZookeeper(springContext);
		this.syncStartupBootServer(springContext);
//		this.createZookeeperNode(springContext);
		
		try {
			synchronized (this) {
				this.wait();
			}
		} catch(Exception e) {
			e.printStackTrace(System.out);
		}
	}
	
	/**
	 * 启动spring上下文服务
	 */
	private static FileSystemXmlApplicationContext startupSpring() {
		//启动数据库连接池的程序是放在spring启动里面的
		String configurationXMLRoot = BootStartup.LOCAL_Environment.get("-C");
		File[] springxmlfiles = new File(configurationXMLRoot).listFiles(new FileFilter() {
			@Override
			public boolean accept(File pathname) {
				Pattern pattern = Pattern.compile("application\\-[\\S]+.xml$");
				String filename = pathname.getName();
				if(pattern.matcher(filename).find()) {
					return true;
				}
				return false;
			}
		});
		if(springxmlfiles == null || springxmlfiles.length == 0) {
			System.out.println("没有找到配置文件 : 请检查启动参数");
			System.exit(0);
		}
		
		String [] springxmlfilepaths = new String[springxmlfiles.length];
		for(int index = 0 ; index < springxmlfiles.length ; index++) {
			springxmlfilepaths[index] = springxmlfiles[index].getAbsolutePath();
		}
		FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext(springxmlfilepaths);
		return context;
	}
	
	/**
	 * 开启zookeeper线程,这里只是注册zookeeper服务，并不是进行zookeeper上节点的增加
	 */
	private void startupZookeeper(ApplicationContext springContext) {
		//这样就可以zookeeper链接的执行初始化了
		springContext.getBean("zookeeperReadManager");
	} 
	
	/**
	 * 向远程zookeeper集群注册分布式事务锁，以便保证多个服务器启动时的服务器顺序依次启动
	 * @param springContext
	 */
	private void syncStartupBootServer(ApplicationContext springContext) {
		ZookeeperServerBootWriteManager bootManager = (ZookeeperServerBootWriteManager)springContext.getBean("zookeeperServerBootWriteManager");
		bootManager.syncBoot();
	}
	
	public StartupNettyUDPBoot getStartupNettyUDPBoot() {
		return startupNettyUDPBoot;
	}

	public void setStartupNettyUDPBoot(StartupNettyUDPBoot startupNettyUDPBoot) {
		this.startupNettyUDPBoot = startupNettyUDPBoot;
	}

	public StartupNettyTCPBoot getStartupNettyTCPBoot() {
		return startupNettyTCPBoot;
	}

	public void setStartupNettyTCPBoot(StartupNettyTCPBoot startupNettyTCPBoot) {
		this.startupNettyTCPBoot = startupNettyTCPBoot;
	}
}
